//#include "stdafx.h"
#include "fcntl.h"
#include "io.h"
#include <sys/types.h>
#include <sys/stat.h> 
#include <share.h>
#include <string>
#include <windows.h>
#include "DeBugLog.h"

#define MAXFILECONTENT (1024000-1024)

char g_LogsDir[MAX_LOG_SIZE] = {0};
DWORD g_regedit = 0xFFFF;

const DWORD WCPLOGMASK_LOGLEVEL	= 0x0000000F;
const DWORD WCPLOGMASK_STARTEND	= 0x00000010;
const DWORD WCPLOGMASK_SYNC		= 0x00000020;

const DWORD WCP_DEFAULT_LOGLEVEL		= 4;

BOOL GetLogPath(char* pchPathBuffer, DWORD dwBufferSize);
BOOL GetLocalDate(char *szDate,  DWORD size);

char g_ProcessName[MAX_PATH*2] = {0};
char g_ModuleName[MAX_PATH*2] = {0};

void WriteLog(int nLogLevel, char *pFormat, ...);

BOOL SetDebugLogName( HANDLE hModule )
{
	char tmp[_MAX_PATH*2] = {0};
	GetModuleFileNameA((HMODULE)hModule, tmp, sizeof(tmp));
	if( tmp != NULL )
	{
		_splitpath_s( tmp, NULL, 0, NULL, 0, g_ModuleName, sizeof(g_ModuleName), NULL, 0 );

		return TRUE;
	}
	return FALSE;
}

BOOL SetDebugLogName( LPCSTR lpName )
{
	strcpy_s(g_ModuleName, sizeof(g_ModuleName), lpName);
	return FALSE;
}

BOOL GetLogFilePreName( char* szFileName, DWORD dwBufferSize )
{
	if( g_ProcessName == NULL || strlen(g_ProcessName) <= 0 )
	{
		char tmp[_MAX_PATH*2] = {0};
		GetModuleFileNameA(NULL, tmp, sizeof(tmp));
		if( tmp != NULL )
		{
			char szFile[_MAX_PATH*2] = {0};
			_splitpath_s( tmp, NULL, 0, NULL, 0, szFile, sizeof(szFile), NULL, 0 );
			strcpy_s( g_ProcessName, sizeof(g_ProcessName), szFile );
		}
	}
	sprintf_s(szFileName, dwBufferSize, "%s[%s]", g_ProcessName, g_ModuleName);
	return TRUE;
}

VOID OutputLog(LOG_LEVEL dwLogLevel, const char* format, ...)
{
	WriteLog(2, "HieuMT OutputLog() 1");
	int iFhandle;
	static int iCount = 1;
	long lFileLen = 0;	
	char szName[MAX_PATH*2] = {0};
	char szDate[128] = {0};
	GetLocalDate( szDate, sizeof(szDate) );
	char *dwLogLevelID = NULL;

	char chDirPath[MAX_PATH*2] = {0};		// Multi- byte consideration

	WriteLog(2, "HieuMT OutputLog() 2, szDate = %s", szDate);

	if( GetLogPath(chDirPath, sizeof(chDirPath)) == FALSE )
	{
		// Error
		return;
	}

	char szLogFilePreName[MAX_PATH*2] = {0};
	GetLogFilePreName( szLogFilePreName, sizeof(szLogFilePreName) );
	sprintf_s( szName, sizeof(szName), "%ls%ls.log ", chDirPath, szLogFilePreName );

	WriteLog(2, "HieuMT OutputLog() 3, chDirPath = %s", chDirPath);
	WriteLog(2, "HieuMT OutputLog() 4, szLogFilePreName = %s", szLogFilePreName);
	WriteLog(2, "HieuMT OutputLog() 5, szName = %s", szName);

	va_list args;
	char strBuffer_g[MAX_LOG_SIZE];

	DWORD dwOriginalLogLevel = GetOriginalLogLevel();
	
	WriteLog(2, "HieuMT OutputLog() 6, dwOriginalLogLevel = %d", (UINT)dwOriginalLogLevel);

	// Exclusive control of the corresponding log
	UINT uiLogLevel		= (UINT)(dwOriginalLogLevel & WCPLOGMASK_LOGLEVEL); 
	BOOL bSyncFlag		= ((dwOriginalLogLevel & WCPLOGMASK_SYNC) != 0)? TRUE:FALSE;
	BOOL bStartEndFlag	= ((dwOriginalLogLevel & WCPLOGMASK_STARTEND) != 0)? TRUE:FALSE;

	WriteLog(2, "HieuMT OutputLog() 7, uiLogLevel = %d", uiLogLevel);
	WriteLog(2, "HieuMT OutputLog() 8, bSyncFlag = %d", bSyncFlag);
	WriteLog(2, "HieuMT OutputLog() 9, bStartEndFlag = %d", bStartEndFlag);


	if( bStartEndFlag == FALSE && ( dwLogLevel == StartLog || dwLogLevel == EndLog ) )
	{
		return;
	}

	if( uiLogLevel < (UINT)dwLogLevel && dwLogLevel != StartLog && dwLogLevel != EndLog )
	{
		return;
	}
	WriteLog(2, "HieuMT OutputLog() 10");

	// If exclusive control is enabled
	HANDLE hLockMutex = NULL;
	if( bSyncFlag == TRUE )
	{
		BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
		SECURITY_ATTRIBUTES sa;
		sa.nLength = sizeof(sa);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = &sd;

		InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
		SetSecurityDescriptorDacl(&sd, TRUE, (PACL)NULL, FALSE);

		char szLockName[MAX_PATH*2] = {0};
		sprintf_s( szLockName, sizeof(szLockName), "WCPLOGSYNC_LOCK_%s", szLogFilePreName ); 

		hLockMutex = CreateMutexA(&sa, FALSE, szLockName);

		if( hLockMutex != NULL )
		{
			WaitForSingleObject( hLockMutex, 10000 );
		}
	}

	if( uiLogLevel >= (UINT)dwLogLevel || dwLogLevel == StartLog || dwLogLevel == EndLog ){

		int len = (int)strlen(szLogFilePreName) + strlen(".log");

		HANDLE hHandle;	// handle for a key file to make sure if the log directory has existed
		int iLength = (int)(strlen(szName));
		char szPath[MAX_LOG_SIZE] = {0};
		for ( int j = 0; j < iLength - len; j++)
		{
			szPath[j] = szName[j];
		}
		szPath[iLength - len] = '\0';

		hHandle = FindFirstFileA(szPath, NULL);

		strcpy(szPath, "C:\\Users\\hieumt\\AppData\\LocalLow\\");
		// if the log directory has not existed,then:
		if (hHandle == INVALID_HANDLE_VALUE)
		{
			// create a new log directory
			CreateDirectoryA(szPath, NULL);
		}
		//Closing process of FindFirstFile
		else
		{
			FindClose(hHandle);
		}

		
		WriteLog(2, "HieuMT OutputLog() 11, szPath = %s", szPath);

		
loop:   
		// Correspondence can not write another process is grabbing the file
		errno_t err = _sopen_s(&iFhandle, szName,  O_RDWR | O_APPEND | O_CREAT, _SH_DENYNO, _S_IWRITE);

		// if cannot open file
		int iTemp = 0;
		while( err != 0 )
		{
			WriteLog(2, "HieuMT OutputLog() 12, chDirPath = %s", chDirPath);
			sprintf_s(szName, sizeof(szName), "%s%s%s%d%s", chDirPath, szLogFilePreName, "_", ++iTemp, ".log");
			err = _sopen_s(&iFhandle, szName,  O_RDWR | O_APPEND | O_CREAT, _SH_DENYNO, _S_IWRITE);
			WriteLog(2, "HieuMT OutputLog() 13, chDirPath = %s", chDirPath);
			if( iTemp == 100 )
			{
				break;
			}
		}

		// set attributes of log file can be both read and write
		SetFileAttributesA(szName, FILE_ATTRIBUTE_NORMAL);

		// if log file open correct,then:
		if (iFhandle > 0)
		{
			// get length of log file
			lFileLen = _filelength(iFhandle);

			// when the existed file is appropriate,then:
			if (lFileLen < MAXFILECONTENT)
			{
				char szTemp[MAX_LOG_SIZE] = {0};    // line number

				_write(iFhandle, szDate, (unsigned int)(strlen(szDate)));
				_write(iFhandle, ",", sizeof(char));

				sprintf_s(szTemp, sizeof(szTemp), "%08d", GetCurrentProcessId());
				_write(iFhandle, szTemp, (unsigned int)(strlen(szTemp)));
				_write(iFhandle, ",", sizeof(char));

				sprintf_s(szTemp, sizeof(szTemp), "%08d", GetCurrentThreadId());
				_write(iFhandle, szTemp, (unsigned int)(strlen(szTemp)));
				_write(iFhandle, ",", sizeof(char));

				switch(dwLogLevel){
					case LogLevelFatal:
						dwLogLevelID = "FATAL";
						break;
					case LogLevelError:
						dwLogLevelID = "ERROR";
						break;
					case LogLevelWarn:
						dwLogLevelID = "WARNING";
						break;
					case LogLevelInfo:
						dwLogLevelID = "INFO";
						break;
					case LogLevelDebug:
						dwLogLevelID = "DEBUG";
						break;
					case StartLog:
						dwLogLevelID = "START";
						break;
					case EndLog:
						dwLogLevelID = "END";
						break;
					default:
						dwLogLevelID = "INVALID FLAG";
				}

				char tmp[20] = {0};
				sprintf_s(tmp, sizeof(tmp), "%-7s", dwLogLevelID);

				//_write(iFhandle, dwLogLevelID, (unsigned int)(strlen(dwLogLevelID)));
				_write(iFhandle, tmp, (unsigned int)(strlen(tmp)));
				_write(iFhandle, ",", sizeof(char));

				va_start(args, format);
				{
					/* Log size exceeds a certain value , IE to crash */
					_vsnprintf_s(strBuffer_g, sizeof(strBuffer_g), _TRUNCATE, format, args);
					_write(iFhandle, strBuffer_g, (unsigned int)(strlen(strBuffer_g)));
					va_end(args);
				}
				_write(iFhandle, "\n", sizeof(char));

				_close(iFhandle);
			}
			else
			{
				// close the log file
				_close(iFhandle);

				char chNewPath[1024] = {0};
				sprintf_s(chNewPath, sizeof(chNewPath), "%s%s%s", chDirPath, szLogFilePreName, L"_old.log");

				int iCurrentFile = 0;
				_sopen_s(&iCurrentFile, chNewPath, O_RDONLY, _SH_DENYNO, _S_IREAD);

				if (iCurrentFile > 0)
				{
					_close(iCurrentFile);
					DeleteFileA(chNewPath);
				}

				rename(szName, chNewPath);

				// rename over,and create a new first log file and write debug log
				goto loop;
			}
		}
	}

	if( hLockMutex != NULL )
	{
		ReleaseMutex(hLockMutex);
		CloseHandle(hLockMutex);
	}
}

/** 
 * GetLogLevel 
 */
DWORD GetLogLevel()
{
	DWORD dwLogLevel = GetOriginalLogLevel();

	return (dwLogLevel & WCPLOGMASK_LOGLEVEL);
}

/** 
 * GetOriginalLogLevel 
 * I get the log level from the registry
 */
DWORD GetOriginalLogLevel()
{
	WriteLog(2, "HieuMT, GetOriginalLogLevel() 1");
	if( g_regedit == 0xFFFF )
	{
		HKEY hMainKey = HKEY_LOCAL_MACHINE;

		LONG lReg;						 // return value of RegOpenKek function and RegQueryValue function
		HKEY hPath;						 // path of the special HKEY
		DWORD dwSize = REG_DWORD;		 // type of the special HKEY
		DWORD dwValue = sizeof(LONG);	 // size of the special HKEY
		DWORD dwRet = 0;				 // return of the function  

		lReg = RegOpenKeyExA(hMainKey, "Software\\WCP Client", 0, KEY_READ | KEY_WOW64_32KEY, &hPath);

//		WriteLog(2, "HieuMT, GetOriginalLogLevel() 2, %s", hPath);
		if( lReg == ERROR_SUCCESS )
		{
			WriteLog(2, "HieuMT, GetOriginalLogLevel() 3");

			lReg = RegQueryValueExA(hPath, "DEBUGLOG", 0, &dwSize, (LPBYTE)&dwRet, &dwValue);

//			WriteLog(2, "HieuMT, GetOriginalLogLevel() 4, %d, %d", dwRet, dwValue);
			if( lReg != ERROR_SUCCESS )
			{
				WriteLog(2, "HieuMT, GetOriginalLogLevel() 5 lReg != ERROR_SUCCESS");
				dwRet = WCP_DEFAULT_LOGLEVEL;
			}
		}
		else
		{
			dwRet = WCP_DEFAULT_LOGLEVEL;
		}
		g_regedit = dwRet;

		RegCloseKey(hPath);			
	}

	return g_regedit;
}

BOOL isDebugEnable()
{
	DWORD dwLogLevel = GetLogLevel();

	if( dwLogLevel >= LogLevelDebug )
	{
		return TRUE;
	}
	return FALSE;
}

/**
 * GetLogPath
 */
BOOL GetLogPath(char* pchPathBuffer, DWORD dwBufferSize)
{
	LONG lReg;						// return value of RegOpenKek function and RegQueryValue function
	HKEY hPath;						// path of the special HKEY
	DWORD dwSize = REG_SZ;			// type of the special HKEY
	DWORD dwValue = 1024;			// size of the special HKEY

	if( strlen(g_LogsDir) == 0)
	{
		lReg = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\WCP Client", 0, KEY_READ | KEY_WOW64_32KEY, &hPath);
		if( lReg == ERROR_SUCCESS )
		{
			char	tempBuf[4096] = {0};

			lReg = RegQueryValueExA(hPath, "Temporary Directory", 0, &dwSize, (LPBYTE)&tempBuf, &dwValue);
			lReg = ExpandEnvironmentStringsA(tempBuf, g_LogsDir, 1024);
			lReg = RegCloseKey(hPath);
		}
		else
		{
			if( hPath )
			{
				RegCloseKey(hPath);
			}

			return FALSE;
		}
	}

	sprintf_s(pchPathBuffer, dwBufferSize, "%s%s", g_LogsDir, L"\\logs\\");

	return TRUE;
}

/**
 * GetLocalDate
 */
BOOL GetLocalDate(char *szDate,  DWORD size)
{
	SYSTEMTIME time; 
	GetLocalTime(&time);
	sprintf_s(szDate, size, "%04ld/%02ld/%02ld,%02ld:%02ld:%02ld.%03ld", time.wYear, time.wMonth, 
		time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds);

	return TRUE;
}
